package io.gitee.mingbaobaba.apijson.querycondition.query.conditions;

import io.gitee.mingbaobaba.apijson.querycondition.query.exception.ConditionException;
import org.apache.commons.lang3.StringUtils;

import java.util.List;
import java.util.Objects;
import java.util.function.Consumer;
import java.util.stream.Collectors;

/**
 * <p>构建参数查询基类</p>
 *
 * @author yingsheng.ye
 * @version 1.0.0
 * @since 2023/6/23 8:27
 */
@SuppressWarnings("unchecked")
public abstract class AbstractQueryWrapper<T, R, C extends AbstractBaseWrapper<T, R, C>> extends AbstractBaseWrapper<T, R, C> {

    /**
     * 子类实现查询列
     *
     * @param columns 查询列
     * @return C
     */
    protected abstract C select(R... columns);

    /**
     * 子类实现查询列
     *
     * @param condition 条件false忽略
     * @param columns   查询列
     * @return C
     */
    protected abstract C select(boolean condition, R... columns);

    /**
     * 添加条件，子类实现
     *
     * @param condition 条件false忽略
     * @param column    列
     * @param keyword   连接条件
     * @param val       值
     * @return Children
     */
    protected abstract C addCondition(boolean condition, R column, EnumKeyword keyword, Object val);

    /**
     * 添加聚合函数，子类实现
     *
     * @param condition 条件false忽略
     * @param column    列
     * @param keyword   连接条件
     * @param val       值
     * @return Children
     */
    protected abstract C addAggFunc(boolean condition, R column, EnumKeyword keyword, Object val);

    /**
     * 添加分组函数，子类实现
     *
     * @param condition 条件false忽略
     * @param column    列
     * @param keyword   连接条件
     * @return Children
     */
    protected abstract C addGroupFunc(boolean condition, R column, EnumKeyword keyword);

    /**
     * 添加排序函数，子类实现
     *
     * @param condition 条件
     * @param column    列
     * @param keyword   关键字
     * @param s         排序值 +升序 -降序
     * @return C
     */
    protected abstract C addOrderByFunc(boolean condition, R column, EnumKeyword keyword, String s);

    /**
     * 子类返回一个自己的新对象
     */
    protected abstract C instance();

    /**
     * 多重嵌套条件处理
     *
     * @param condition 条件
     * @param keyword   关键字
     * @param consumer  Consumer
     */
    private void addNestedCondition(boolean condition, EnumKeyword keyword, Consumer<C> consumer) {
        if (condition) {
            final C instance = instance();
            consumer.accept(instance);
            //检查嵌套条件
            if (Objects.nonNull(instance.joinOperation)) {
                throw new ConditionException("条件嵌套不支持join");
            }
            //获取条件
            List<Condition> cList = instance.conditionList.stream().filter(item -> !item.getKeyword().equals(EnumKeyword.AND)
                    && !item.getKeyword().equals(EnumKeyword.OR) && !item.getKeyword().equals(EnumKeyword.NOT)).collect(Collectors.toList());
            //条件转换
            cList.forEach(cd -> {
                //判断字段是否存在
                if (this.conditionList.stream().noneMatch(item ->
                        StringUtils.isNotBlank(item.getColumn()) &&
                                item.getColumn().equals(cd.getColumn()))) {
                    this.conditionList.add(new Condition(null, keyword, null));
                    this.conditionList.add(cd);
                }
            });
        }
    }

    /**
     * 且
     *
     * @param condition 条件
     * @return C
     */
    public C and(boolean condition) {
        return addCondition(condition, null, EnumKeyword.AND, null);
    }

    /**
     * 且
     *
     * @return C
     */
    public C and() {
        return addCondition(true, null, EnumKeyword.AND, null);
    }

    /**
     * 且
     *
     * @param condition 条件
     * @param consumer  新对象
     * @return C
     */
    public C and(boolean condition, Consumer<C> consumer) {
        addNestedCondition(condition, EnumKeyword.AND, consumer);
        return typedThis;
    }

    /**
     * 且
     *
     * @param consumer 新对象
     * @return C
     */
    public C and(Consumer<C> consumer) {
        return and(true, consumer);
    }

    /**
     * 或
     *
     * @param condition 条件
     * @return C
     */
    public C or(boolean condition) {
        return addCondition(condition, null, EnumKeyword.OR, null);
    }

    /**
     * 或
     *
     * @return C
     */
    public C or() {
        return addCondition(true, null, EnumKeyword.OR, null);
    }

    /**
     * 或
     *
     * @param condition 条件
     * @param consumer  新对象
     * @return C
     */
    public C or(boolean condition, Consumer<C> consumer) {
        addNestedCondition(condition, EnumKeyword.OR, consumer);
        return typedThis;
    }

    /**
     * 或
     *
     * @param consumer 新对象
     * @return C
     */
    public C or(Consumer<C> consumer) {
        return or(true, consumer);
    }

    /**
     * 非
     *
     * @return C
     */
    public C not() {
        return addCondition(true, null, EnumKeyword.NOT, null);
    }

    /**
     * 非
     *
     * @return C
     */
    public C not(boolean condition) {
        return addCondition(condition, null, EnumKeyword.NOT, null);
    }

    /**
     * 非
     *
     * @return C
     */
    public C not(boolean condition, Consumer<C> consumer) {
        addNestedCondition(condition, EnumKeyword.NOT, consumer);
        return typedThis;
    }

    /**
     * 非
     *
     * @return C
     */
    public C not(Consumer<C> consumer) {
        return not(true, consumer);
    }

    /**
     * 列为空
     *
     * @param column 列
     * @return C
     */
    public C isNull(R column) {
        return isNull(true, column);
    }

    /**
     * 列为空
     *
     * @param condition 条件
     * @param column    列
     * @return C
     */
    public C isNull(boolean condition, R column) {
        return addCondition(condition, column, EnumKeyword.NULL, "=null");
    }

    /**
     * 列不为空
     *
     * @param column 列
     * @return C
     */
    public C isNotNull(R column) {
        return isNotNull(true, column);
    }

    /**
     * 列不为空
     *
     * @param column 列
     * @return C
     */
    public C isNotNull(boolean condition, R column) {
        return addCondition(condition, column, EnumKeyword.NOT_NULL, "!=null");
    }

    /**
     * in
     *
     * @param condition 条件
     * @param column    列
     * @param val       值
     * @return C
     */
    public C in(boolean condition, R column, Object val) {
        return addCondition(condition, column, EnumKeyword.IN, val);
    }

    /**
     * in
     *
     * @param column 列
     * @param val    值
     * @return C
     */
    public C in(R column, Object val) {
        return addCondition(true, column, EnumKeyword.IN, val);
    }

    /**
     * not in
     *
     * @param condition 条件
     * @param column    列
     * @param val       值
     * @return C
     */
    public C notIn(boolean condition, R column, Object val) {
        return addCondition(condition, column, EnumKeyword.NOT_IN, val);
    }

    /**
     * not in
     *
     * @param column 列
     * @param val    值
     * @return C
     */
    public C notIn(R column, Object val) {
        return addCondition(true, column, EnumKeyword.NOT_IN, val);
    }

    /**
     * 模糊匹配
     *
     * @param condition 条件
     * @param column    列
     * @param val       值
     * @return C
     */
    public C like(boolean condition, R column, Object val) {
        return addCondition(condition, column, EnumKeyword.LIKE, "%" + val + "%");
    }

    /**
     * 模糊匹配
     *
     * @param column 列
     * @param val    值
     * @return C
     */
    public C like(R column, Object val) {
        return addCondition(true, column, EnumKeyword.LIKE, "%" + val + "%");
    }

    /**
     * 左模糊匹配
     *
     * @param condition 条件
     * @param column    列
     * @param val       值
     * @return C
     */
    public C leftLike(boolean condition, R column, Object val) {
        return addCondition(condition, column, EnumKeyword.LIKE, "%" + val);
    }

    /**
     * 左模糊匹配
     *
     * @param column 列
     * @param val    值
     * @return C
     */
    public C leftLike(R column, Object val) {
        return addCondition(true, column, EnumKeyword.LIKE, "%" + val);
    }

    /**
     * 右模糊匹配
     *
     * @param condition 条件
     * @param column    列
     * @param val       值
     * @return C
     */
    public C rightLike(boolean condition, R column, Object val) {
        return addCondition(condition, column, EnumKeyword.LIKE, val + "%");
    }

    /**
     * 右模糊匹配
     *
     * @param column 列
     * @param val    值
     * @return C
     */
    public C rightLike(R column, Object val) {
        return addCondition(true, column, EnumKeyword.LIKE, val + "%");
    }

    /**
     * 过滤模糊匹配
     *
     * @param condition 条件
     * @param column    列
     * @param val       值
     * @return C
     */
    public C notLike(boolean condition, R column, Object val) {
        return addCondition(condition, column, EnumKeyword.NOT_LIKE, "%" + val + "%");
    }

    /**
     * 过滤模糊匹配
     *
     * @param column 列
     * @param val    值
     * @return C
     */
    public C notLike(R column, Object val) {
        return addCondition(true, column, EnumKeyword.NOT_LIKE, "%" + val + "%");
    }

    /**
     * 等于
     *
     * @param condition 条件
     * @param column    列
     * @param val       值
     * @return C
     */
    public C eq(boolean condition, R column, Object val) {
        return addCondition(condition, column, EnumKeyword.EQ, val);
    }

    /**
     * 等于
     *
     * @param column 列
     * @param val    值
     * @return C
     */
    public C eq(R column, Object val) {
        return addCondition(true, column, EnumKeyword.EQ, val);
    }

    /**
     * 不等于
     *
     * @param condition 条件
     * @param column    列
     * @param val       值
     * @return C
     */
    public C ne(boolean condition, R column, Object val) {
        return addCondition(condition, column, EnumKeyword.NE, val);
    }

    /**
     * 不等于
     *
     * @param column 列
     * @param val    值
     * @return C
     */
    public C ne(R column, Object val) {
        return addCondition(true, column, EnumKeyword.NE, val);
    }

    /**
     * 大于等于
     *
     * @param condition 条件
     * @param column    列
     * @param val       值
     * @return C
     */
    public C ge(boolean condition, R column, Object val) {
        return addCondition(condition, column, EnumKeyword.GE, val);
    }

    /**
     * 大于等于
     *
     * @param column 列
     * @param val    值
     * @return C
     */
    public C ge(R column, Object val) {
        return addCondition(true, column, EnumKeyword.GE, val);
    }

    /**
     * 大于
     *
     * @param condition 条件
     * @param column    列
     * @param val       值
     * @return C
     */
    public C gt(boolean condition, R column, Object val) {
        return addCondition(condition, column, EnumKeyword.GT, val);
    }

    /**
     * 大于
     *
     * @param column 列
     * @param val    值
     * @return C
     */
    public C gt(R column, Object val) {
        return addCondition(true, column, EnumKeyword.GT, val);
    }

    /**
     * 小于等于
     *
     * @param condition 条件
     * @param column    列
     * @param val       值
     * @return C
     */
    public C le(boolean condition, R column, Object val) {
        return addCondition(condition, column, EnumKeyword.LE, val);
    }

    /**
     * 小于等于
     *
     * @param column 列
     * @param val    值
     * @return C
     */
    public C le(R column, Object val) {
        return addCondition(true, column, EnumKeyword.LE, val);
    }

    /**
     * 小于
     *
     * @param condition 条件
     * @param column    列
     * @param val       值
     * @return C
     */
    public C lt(boolean condition, R column, Object val) {
        return addCondition(condition, column, EnumKeyword.LT, val);
    }

    /**
     * 小于
     *
     * @param column 列
     * @param val    值
     * @return C
     */
    public C lt(R column, Object val) {
        return addCondition(true, column, EnumKeyword.LT, val);
    }

    /**
     * 正则表达式匹配
     *
     * @param column 列
     * @param exp    表达式
     * @return C
     */
    public C regExp(boolean condition, R column, String exp) {
        return addCondition(condition, column, EnumKeyword.REG_EXP, exp);
    }

    /**
     * 正则表达式匹配
     *
     * @param column 列
     * @param exp    表达式
     * @return C
     */
    public C regExp(R column, String exp) {
        return addCondition(true, column, EnumKeyword.REG_EXP, exp);
    }

    /**
     * 范围内匹配
     *
     * @param condition 条件
     * @param column    列
     * @param start     起始值
     * @param end       结束值
     * @return C
     */
    public C between(boolean condition, R column, Object start, Object end) {
        return addCondition(condition, column, EnumKeyword.BETWEEN_AND, start + "," + end);
    }

    /**
     * 范围内匹配
     *
     * @param column 列
     * @param start  起始值
     * @param end    结束值
     * @return C
     */
    public C between(R column, Object start, Object end) {
        return addCondition(true, column, EnumKeyword.BETWEEN_AND, start + "," + end);
    }

    /**
     * 升序排序
     *
     * @param condition 条件
     * @param column    列
     * @return C
     */
    public C orderByAsc(boolean condition, R column) {
        return orderBy(condition, column, true);
    }

    /**
     * 升序排序
     *
     * @param column 列
     * @return C
     */
    public C orderByAsc(R column) {
        return orderBy(column, true);
    }

    /**
     * 降序排序
     *
     * @param condition 条件
     * @param column    列
     * @return C
     */
    public C orderByDesc(boolean condition, R column) {
        return orderBy(condition, column, false);
    }

    /**
     * 降序排序
     *
     * @param column 列
     * @return C
     */
    public C orderByDesc(R column) {
        return orderBy(column, false);
    }

    /**
     * 排序
     *
     * @param condition 条件
     * @param column    列
     * @param isAsc     是否升序
     * @return C
     */
    public C orderBy(boolean condition, R column, boolean isAsc) {
        return addOrderByFunc(condition, column, EnumKeyword.ORDER_BY, isAsc ? EnumOrder.ASC.getCode() : EnumOrder.DESC.getCode());
    }

    /**
     * 排序
     *
     * @param column 列
     * @param isAsc  是否升序
     * @return C
     */
    public C orderBy(R column, boolean isAsc) {
        return orderBy(true, column, isAsc);
    }

    /**
     * 统计
     *
     * @param column 列
     * @param alias  别名
     * @return C
     */
    public C count(R column, String alias) {
        return addAggFunc(true, column, EnumKeyword.COUNT, alias);
    }

    /**
     * 统计
     *
     * @param condition 条件
     * @param column    列
     * @param alias     别名
     * @return C
     */
    public C count(boolean condition, R column, String alias) {
        return addAggFunc(condition, column, EnumKeyword.COUNT, alias);
    }

    /**
     * 汇总
     *
     * @param column 列
     * @param alias  别名
     * @return C
     */
    public C sum(R column, String alias) {
        return addAggFunc(true, column, EnumKeyword.SUM, alias);
    }

    /**
     * 汇总
     *
     * @param condition 条件
     * @param column    列
     * @param alias     别名
     * @return C
     */
    public C sum(boolean condition, R column, String alias) {
        return addAggFunc(condition, column, EnumKeyword.SUM, alias);
    }

    /**
     * 最大值
     *
     * @param column 列
     * @param alias  别名
     * @return C
     */
    public C max(R column, String alias) {
        return addAggFunc(true, column, EnumKeyword.MAX, alias);
    }

    /**
     * 最大值
     *
     * @param condition 条件
     * @param column    列
     * @param alias     别名
     * @return C
     */
    public C max(boolean condition, R column, String alias) {
        return addAggFunc(condition, column, EnumKeyword.MAX, alias);
    }

    /**
     * 最小值
     *
     * @param column 列
     * @param alias  别名
     * @return C
     */
    public C min(R column, String alias) {
        return addAggFunc(true, column, EnumKeyword.MIN, alias);
    }

    /**
     * 最小值
     *
     * @param condition 条件
     * @param column    列
     * @param alias     别名
     * @return C
     */
    public C min(boolean condition, R column, String alias) {
        return addAggFunc(condition, column, EnumKeyword.MIN, alias);
    }

    /**
     * 平均值
     *
     * @param column 列
     * @param alias  别名
     * @return C
     */
    public C avg(R column, String alias) {
        return addAggFunc(true, column, EnumKeyword.AVG, alias);
    }

    /**
     * 平均值
     *
     * @param condition 条件
     * @param column    列
     * @param alias     别名
     * @return C
     */
    public C avg(boolean condition, R column, String alias) {
        return addAggFunc(condition, column, EnumKeyword.AVG, alias);
    }

    /**
     * 分组
     *
     * @param condition 条件
     * @param column    列
     * @return C
     */
    public C group(boolean condition, R column) {
        return addGroupFunc(condition, column, EnumKeyword.GROUP);
    }

    /**
     * having
     *
     * @param func SQL函数条件，一般和@group一起用，函数一般在@column里声明
     *             <p>
     *             示例：
     *             查询 按userId分组、id最大值>=100 的Moment数组：
     *             "@column":"userId;max(id)",
     *             "@group":"userId",
     *             "@having":"max(id)>=100"(opens new window)
     *             对应SQL是SELECT userId,max(id) ... GROUP BY userId HAVING max(id)>=100
     *             还可以指定函数返回名：
     *             "@column":"userId;max(id):maxId",
     *             "@group":"userId",
     *             "@having":"maxId>=100"(opens new window)
     *             对应SQL是SELECT userId,max(id) AS maxId ... GROUP BY userId HAVING maxId>=100
     *             </p>
     * @return C
     */
    protected C having(String func) {
        this.havingFunc = func;
        return typedThis;
    }


    /**
     * 拼接参数
     *
     * @param condition 条件
     * @param key       key值
     * @param value     value值
     * @return C
     */
    public C apply(boolean condition, EnumApiJson key, String value) {
        if (condition) {
            this.lastAppendMap.put(key.getCode(), value);
        }
        return typedThis;
    }

    /**
     * 拼接参数
     *
     * @param key   key值
     * @param value value值
     * @return C
     */
    public C apply(EnumApiJson key, String value) {
        return apply(true, key, value);
    }

    /**
     * 分组
     *
     * @param column 列
     * @return C
     */
    public C group(R column) {
        return addGroupFunc(true, column, EnumKeyword.GROUP);
    }

    /**
     * 聚合函数处理
     *
     * @param dwKeyword 关键字
     * @param column    列名
     * @param alias     别名
     */
    protected void aggFuncHandle(EnumKeyword dwKeyword, String column, Object alias) {
        switch (dwKeyword) {
            case COUNT:
                this.columnList.add("count(" + column + "):" + alias);
                break;
            case SUM:
                this.columnList.add("sum(" + column + "):" + alias);
                break;
            case MAX:
                this.columnList.add("max(" + column + "):" + alias);
                break;
            case MIN:
                this.columnList.add("min(" + column + "):" + alias);
                break;
            case AVG:
                this.columnList.add("avg(" + column + "):" + alias);
                break;
            default:
        }
    }

    /**
     * 分组函数处理
     *
     * @param keyword 关键字
     * @param column  列名
     */
    protected void groupFuncHandle(EnumKeyword keyword, String column) {
        if (keyword.equals(EnumKeyword.GROUP)) {
            this.groupList.add(column);
        }
    }

    /**
     * 条件处理
     *
     * @param condition 条件
     */
    protected void conditionHandle(Condition condition) {
        this.conditionList.add(condition);
    }

    /**
     * 排序处理
     *
     * @param keyword 关键字
     * @param column  排序列
     */
    protected void orderByFuncHandle(EnumKeyword keyword, String column) {
        if (keyword.equals(EnumKeyword.ORDER_BY)) {
            this.orderByList.add(column);
        }
    }

    /**
     * join 实例
     *
     * @param <E> 泛型
     * @return 实例化join
     */
    protected abstract <E> JoinWrapper<E> joinInstance();

    /**
     * 分页
     *
     * @param condition 条件
     * @param page      页数
     * @param limit     分页条数
     * @return C
     */
    public C page(boolean condition, Integer page, Integer limit) {
        if (condition) {
            if (null == page || null == limit) {
                throw new ConditionException("page条件page和limit参数不能为空");
            }
            pageWrapper = new PageWrapper(page, limit);
        }
        return typedThis;
    }

    /**
     * 分页
     *
     * @param page  页数
     * @param limit 分页条数
     * @return C
     */
    public C page(Integer page, Integer limit) {
        return page(true, page, limit);
    }

    /**
     * 分页
     *
     * @param condition 条件
     * @param limit     分页条数
     * @return C
     */
    public C page(boolean condition, Integer limit) {
        return page(condition, 0, limit);
    }

    /**
     * 分页
     *
     * @param limit 分页条数
     * @return C
     */
    public C page(Integer limit) {
        return page(true, 0, limit);
    }
}
